/*
 * Copyright (c) 2016 Kevin Herron
 *
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * and Eclipse Distribution License v1.0 which accompany this distribution.
 *
 * The Eclipse Public License is available at
 *   http://www.eclipse.org/legal/epl-v10.html
 * and the Eclipse Distribution License is available at
 *   http://www.eclipse.org/org/documents/edl-v10.html.
 */

package com.dayu.unitalk.ua.server;

import static com.google.common.collect.Lists.newArrayList;
import static org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_ANONYMOUS;
import static org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig.USER_TOKEN_POLICY_USERNAME;

import java.io.File;
import java.util.EnumSet;
import java.util.concurrent.CompletableFuture;

import org.eclipse.milo.opcua.sdk.server.OpcUaServer;
import org.eclipse.milo.opcua.sdk.server.api.config.OpcUaServerConfig;
import org.eclipse.milo.opcua.sdk.server.identity.UsernameIdentityValidator;
import org.eclipse.milo.opcua.stack.core.application.DefaultCertificateManager;
import org.eclipse.milo.opcua.stack.core.application.DefaultCertificateValidator;
import org.eclipse.milo.opcua.stack.core.security.SecurityPolicy;
import org.eclipse.milo.opcua.stack.core.types.builtin.DateTime;
import org.eclipse.milo.opcua.stack.core.types.builtin.LocalizedText;
import org.eclipse.milo.opcua.stack.core.types.structured.BuildInfo;
import org.eclipse.milo.opcua.stack.core.util.CryptoRestrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.collect.ImmutableList;

public class UnitalkUaServer {

	private static final int DEFAULT_OPC_UA_PORT = 12686;
	private static final String APPLICATION_ARTIFACT_ID = "unitalk";
	public static final String APPLICATION_URI = "urn:hddznet:unitalk:ua:server";
	public static final String APPLICATION_NAME = "Unitalk UA Server";

	private static final Logger LOGGER = LoggerFactory.getLogger(UnitalkUaServer.class);

	public static void main(String[] args) throws Exception {

		UnitalkUaServer server = new UnitalkUaServer();

		server.startup().get();

		final CompletableFuture<Void> future = new CompletableFuture<>();

		Runtime.getRuntime().addShutdownHook(new Thread(() -> future.complete(null)));

		future.get();
	}

	private final OpcUaServer server;

	public UnitalkUaServer(int port) throws Exception {

		CryptoRestrictions.remove();

		KeyStoreLoader loader = new KeyStoreLoader().load();

		DefaultCertificateManager certificateManager = new DefaultCertificateManager(loader.getServerKeyPair(),
				loader.getServerCertificate());

		File securityTempDir = new File(System.getProperty("java.io.tmpdir"), "security");

		LOGGER.info("security dir: {}", securityTempDir.getAbsolutePath());

		DefaultCertificateValidator certificateValidator = new DefaultCertificateValidator(securityTempDir);

		UsernameIdentityValidator identityValidator = new UsernameIdentityValidator(true, authChallenge -> {

			String username = authChallenge.getUsername();
			String password = authChallenge.getPassword();

			boolean userOk = "user".equals(username) && "password1".equals(password);
			boolean adminOk = "admin".equals(username) && "password2".equals(password);

			return userOk || adminOk;
		});

		OpcUaServerConfig serverConfig = OpcUaServerConfig.builder().setApplicationUri(APPLICATION_URI)
				.setApplicationName(LocalizedText.english(APPLICATION_NAME)).setBindAddresses(newArrayList("0.0.0.0"))
				.setBindPort(port)
				.setBuildInfo(new BuildInfo(APPLICATION_URI, APPLICATION_ARTIFACT_ID, "", OpcUaServer.SDK_VERSION, "",
						DateTime.now()))
				.setCertificateManager(certificateManager).setCertificateValidator(certificateValidator)
				.setIdentityValidator(identityValidator).setProductUri(APPLICATION_URI)
				.setServerName(APPLICATION_ARTIFACT_ID)
				.setSecurityPolicies(EnumSet.of(SecurityPolicy.None, SecurityPolicy.Basic128Rsa15,
						SecurityPolicy.Basic256, SecurityPolicy.Basic256Sha256))
				.setUserTokenPolicies(ImmutableList.of(USER_TOKEN_POLICY_ANONYMOUS, USER_TOKEN_POLICY_USERNAME))
				.build();

		server = new OpcUaServer(serverConfig);

		server.getNamespaceManager().registerAndAdd(UnitalkNamespace.NAMESPACE_URI,
				idx -> new UnitalkNamespace(server, idx));

		// server.getServer().addRequestHandler(TestStackRequest.class, service -> {
		// TestStackRequest request = service.getRequest();
		// ResponseHeader header = service.createResponseHeader();
		// service.setResponse(new TestStackResponse(header, request.getInput()));
		// });
		//
		// server.getServer().addRequestHandler(TestStackExRequest.class, service -> {
		// TestStackExRequest request = service.getRequest();
		// ResponseHeader header = service.createResponseHeader();
		// service.setResponse(new TestStackExResponse(header, request.getInput()));
	}

	public UnitalkUaServer() throws Exception {

		this(DEFAULT_OPC_UA_PORT);
	}

	public OpcUaServer getServer() {
		return server;
	}

	public CompletableFuture<OpcUaServer> startup() {
		return server.startup();
	}

	public CompletableFuture<OpcUaServer> shutdown() {
		return server.shutdown();
	}
}
